home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / normal.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  41KB  |  1,935 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * Contains the main routine for processing characters in command mode.
  11.  * Communicates closely with the code in ops.c to handle the operators.
  12.  */
  13.  
  14. #include "vim.h"
  15. #include "globals.h"
  16. #include "proto.h"
  17. #include "param.h"
  18.  
  19. #undef EXTERN
  20. #undef INIT
  21. #define EXTERN
  22. #define INIT(x) x
  23. #include "ops.h"
  24.  
  25. /*
  26.  * Generally speaking, every command in normal() should either clear any
  27.  * pending operator (with CLEAROP), or set the motion type variable.
  28.  */
  29.  
  30. #define CLEAROP (operator = NOP)        /* clear any pending operator */
  31. #define CLEAROPBEEP     clearopbeep()    /* CLEAROP plus a beep() */
  32. #define CHECKCLEAROP    if (checkclearop()) break;
  33. #define CHECKCLEAROPQ    if (checkclearopq()) break;
  34.  
  35. /*
  36.  * If a count is given before the operator, it is saved in opnum.
  37.  */
  38. static linenr_t    opnum = 0;
  39. static linenr_t    Prenum;         /* The (optional) number before a command. */
  40. int                redo_Visual_busy = FALSE;    /* TRUE when redo-ing a visual */
  41.  
  42. static void        prep_redo __ARGS((long, int, int, int));
  43. static int        checkclearop __ARGS((void));
  44. static int        checkclearopq __ARGS((void));
  45. static void        clearopbeep __ARGS((void));
  46. static void        premsg __ARGS((int, int));
  47.  
  48. extern int        restart_edit;    /* this is in edit.c */
  49.  
  50. /*
  51.  * normal
  52.  *
  53.  * Execute a command in normal mode.
  54.  *
  55.  * This is basically a big switch with the cases arranged in rough categories
  56.  * in the following order:
  57.  *
  58.  *      0. Macros (q, @)
  59.  *      1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
  60.  *      2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
  61.  *      3. Cursor motions (G, H, M, L, l, K_RARROW,  , h, K_LARROW, ^H, k, K_UARROW, ^P, +, CR, LF, j, K_DARROW, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
  62.  *      4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
  63.  *      5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
  64.  *      6. Inserts (A, a, I, i, o, O, R)
  65.  *      7. Operators (~, d, c, y, >, <, !, =, Q)
  66.  *      8. Abbreviations (x, X, D, C, s, S, Y, &)
  67.  *      9. Marks (m, ', `, ^O, ^I)
  68.  *     10. Buffer setting (")
  69.  *     11. Visual (v, V, ^V)
  70.  *   12. Suspend (^Z)
  71.  *   13. Window commands (^W)
  72.  *   14. extended commands (starting with 'g')
  73.  */
  74.  
  75.     void
  76. normal()
  77. {
  78.     register int    c;
  79.     long             n;
  80.     int                flag = FALSE;
  81.     int                flag2 = FALSE;
  82.     int             type = 0;                /* type of operation */
  83.     int             dir = FORWARD;            /* search direction */
  84.     int                nchar = NUL;
  85.     int                finish_op;
  86.     linenr_t        Prenum1;
  87.     char_u            searchbuff[CMDBUFFSIZE];/* buffer for search string */
  88.     FPOS            *pos = NULL;            /* init for gcc */
  89.     register char_u    *ptr;
  90.     int                command_busy = FALSE;
  91.     static int        didwarn = FALSE;        /* warned for broken inversion */
  92.     int                modified = FALSE;        /* changed current buffer */
  93.     int                ctrl_w = FALSE;            /* got CTRL-W command */
  94.  
  95.         /* the visual area is remembered for reselection */
  96.     static linenr_t    resel_Visual_nlines;        /* number of lines */
  97.     static int        resel_Visual_type = 0;    /* type 'v', 'V' or CTRL-V */
  98.     static colnr_t    resel_Visual_col;        /* number of columns or end column */
  99.         /* the visual area is remembered for redo */
  100.     static linenr_t    redo_Visual_nlines;        /* number of lines */
  101.     static int        redo_Visual_type = 0;    /* type 'v', 'V' or CTRL-V */
  102.     static colnr_t    redo_Visual_col;        /* number of columns or end column */
  103.     static long        redo_Visual_Prenum;        /* Prenum for operator */
  104.  
  105.     Prenum = 0;
  106.     /*
  107.      * If there is an operator pending, then the command we take this time
  108.      * will terminate it. Finish_op tells us to finish the operation before
  109.      * returning this time (unless the operation was cancelled).
  110.      */
  111.     finish_op = (operator != NOP);
  112.  
  113.     if (!finish_op && !yankbuffer)
  114.         opnum = 0;
  115.  
  116.     if (p_sc && (vpeekc() == NUL || KeyTyped == TRUE))
  117.         premsg(NUL, NUL);
  118.     State = NORMAL_BUSY;
  119.     c = vgetc();
  120.  
  121. getcount:
  122.     /* Pick up any leading digits and compute 'Prenum' */
  123.     while ((c >= '1' && c <= '9') || (Prenum != 0 && (c == DEL || c == '0')))
  124.     {
  125.         if (c == DEL)
  126.                 Prenum /= 10;
  127.         else
  128.                 Prenum = Prenum * 10 + (c - '0');
  129.         if (Prenum < 0)            /* got too large! */
  130.             Prenum = 999999999;
  131.         premsg(ctrl_w ? Ctrl('W') : ' ', NUL);
  132.         c = vgetc();
  133.     }
  134.  
  135. /*
  136.  * If we got CTRL-W there may be a/another count
  137.  */
  138.     if (c == Ctrl('W') && !ctrl_w)
  139.     {
  140.         ctrl_w = TRUE;
  141.         opnum = Prenum;                        /* remember first count */
  142.         Prenum = 0;
  143.         State = ONLYKEY;                    /* no mapping for nchar, but keys */
  144.         premsg(c, NUL);
  145.         c = vgetc();                        /* get next character */
  146.         goto getcount;                        /* jump back */
  147.     }
  148.  
  149.     /*
  150.      * If we're in the middle of an operator (including after entering a yank
  151.      * buffer with ") AND we had a count before the
  152.      * operator, then that count overrides the current value of Prenum. What
  153.      * this means effectively, is that commands like "3dw" get turned into
  154.      * "d3w" which makes things fall into place pretty neatly.
  155.      * If you give a count before AND after the operator, they are multiplied.
  156.      */
  157.     if (opnum != 0)
  158.     {
  159.             if (Prenum)
  160.                 Prenum *= opnum;
  161.             else
  162.                 Prenum = opnum;
  163.             opnum = 0;
  164.     }
  165.  
  166.     Prenum1 = (Prenum == 0 ? 1 : Prenum);        /* Prenum often defaults to 1 */
  167.     premsg(c, NUL);
  168.  
  169.     /*
  170.      * get an additional character if we need one
  171.      * for CTRL-W we already got it when looking for a count
  172.      */
  173.     if (ctrl_w)
  174.     {
  175.         nchar = c;
  176.         c = Ctrl('W');
  177.         premsg(c, nchar);
  178.     }
  179.     else if (strchr("@zZtTfF[]mg'`\"", c) || (c == 'q' && !Recording && !Exec_reg) ||
  180.                                         (c == 'r' && !VIsual.lnum))
  181.     {
  182.         State = NOMAPPING;
  183.         nchar = vgetc();        /* no macro mapping for this char */
  184.         premsg(c, nchar);
  185.     }
  186.     if (p_sc)
  187.         flushbuf();        /* flush the premsg() characters onto the screen so we can
  188.                             see them while the command is being executed */
  189.  
  190. /*
  191.  * For commands that don't get another character we can put the State back to
  192.  * NORMAL and check for a window size change.
  193.  */
  194.     if (STRCHR("z:/?", c) == NULL)
  195.         State = NORMAL;
  196.     if (nchar == ESC)
  197.     {
  198.         CLEAROP;
  199.         goto normal_end;
  200.     }
  201.     switch (c)
  202.     {
  203.  
  204. /*
  205.  * 0: Macros
  206.  */
  207.       case 'q':         /* (stop) recording into a named register */
  208.         CHECKCLEAROP;
  209.                         /* command is ignored while executing a register */
  210.         if (!Exec_reg && dorecord(nchar) == FAIL)
  211.             CLEAROPBEEP;
  212.         break;
  213.  
  214.      case '@':            /* execute a named buffer */
  215.         CHECKCLEAROP;
  216.         while (Prenum1--)
  217.         {
  218.             if (doexecbuf(nchar) == FAIL)
  219.             {
  220.                 CLEAROPBEEP;
  221.                 break;
  222.             }
  223.         }
  224.         break;
  225.  
  226. /*
  227.  * 1: Screen positioning commands
  228.  */
  229.       case Ctrl('D'):
  230.         flag = TRUE;
  231.  
  232.       case Ctrl('U'):
  233.         CHECKCLEAROP;
  234.         if (Prenum)
  235.             curwin->w_p_scroll = (Prenum > curwin->w_height) ? curwin->w_height : Prenum;
  236.         n = (curwin->w_p_scroll <= curwin->w_height) ? curwin->w_p_scroll : curwin->w_height;
  237.         if (flag)
  238.         {
  239.                 curwin->w_topline += n;
  240.                 if (curwin->w_topline > curbuf->b_ml.ml_line_count)
  241.                     curwin->w_topline = curbuf->b_ml.ml_line_count;
  242.                 comp_Botline(curwin);        /* compute curwin->w_botline */
  243.                 (void)onedown(n);
  244.         }
  245.         else
  246.         {
  247.                 if (n >= curwin->w_cursor.lnum)
  248.                     n = curwin->w_cursor.lnum - 1;
  249.                 Prenum1 = curwin->w_cursor.lnum - n;
  250.                 scrolldown(n);
  251.                 if (Prenum1 < curwin->w_cursor.lnum)
  252.                     curwin->w_cursor.lnum = Prenum1;
  253.         }
  254.         beginline(TRUE);
  255.         updateScreen(VALID);
  256.         break;
  257.  
  258.       case Ctrl('B'):
  259.       case K_SUARROW:
  260.         dir = BACKWARD;
  261.  
  262.       case Ctrl('F'):
  263.       case K_SDARROW:
  264.         CHECKCLEAROP;
  265.         (void)onepage(dir, Prenum1);
  266.         break;
  267.  
  268.       case Ctrl('E'):
  269.         CHECKCLEAROP;
  270.         scrollup(Prenum1);
  271.                 /* We may have moved to another line -- webb */
  272.         coladvance(curwin->w_curswant);
  273.         updateScreen(VALID);
  274.         break;
  275.  
  276.       case Ctrl('Y'):
  277.         CHECKCLEAROP;
  278.         scrolldown(Prenum1);
  279.                 /* We may have moved to another line -- webb */
  280.         coladvance(curwin->w_curswant);
  281.         updateScreen(VALID);
  282.         break;
  283.  
  284.       case 'z':
  285.         CHECKCLEAROP;
  286.         if (isdigit(nchar))
  287.         {
  288.             /*
  289.              * we misuse some variables to be able to call premsg()
  290.              */
  291.             operator = c;
  292.             opnum = Prenum;
  293.             Prenum = nchar - '0';
  294.             for (;;)
  295.             {
  296.                 premsg(' ', NUL);
  297.                 nchar = vgetc();
  298.                 State = NORMAL;
  299.                 if (nchar == DEL)
  300.                     Prenum /= 10;
  301.                 else if (isdigit(nchar))
  302.                     Prenum = Prenum * 10 + (nchar - '0');
  303.                 else if (nchar == CR)
  304.                 {
  305.                     win_setheight((int)Prenum);
  306.                     break;
  307.                 }
  308.                 else
  309.                 {
  310.                     CLEAROPBEEP;
  311.                     break;
  312.                 }
  313.             }
  314.             operator = NOP;
  315.             break;
  316.         }
  317.  
  318.         if (Prenum && Prenum != curwin->w_cursor.lnum)    /* line number given */
  319.         {
  320.             setpcmark();
  321.             if (Prenum > curbuf->b_ml.ml_line_count)
  322.                 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
  323.             else
  324.                 curwin->w_cursor.lnum = Prenum;
  325.         }
  326.         State = NORMAL;            /* for updateScreen() */
  327.         switch (nchar)
  328.         {
  329.           case NL:                /* put curwin->w_cursor at top of screen */
  330.           case CR:
  331.             beginline(TRUE);
  332.           case 't':
  333.             curwin->w_topline = curwin->w_cursor.lnum;
  334.             break;
  335.  
  336.           case '.':             /* put curwin->w_cursor in middle of screen */
  337.           case 'z':
  338.             n = (curwin->w_height + plines(curwin->w_cursor.lnum)) / 2;
  339.             goto dozcmd;
  340.  
  341.           case '-':             /* put curwin->w_cursor at bottom of screen */
  342.           case 'b':
  343.             n = curwin->w_height;
  344.             /* FALLTHROUGH */
  345.  
  346.     dozcmd:
  347.             {
  348.                 register linenr_t    lp = curwin->w_cursor.lnum;
  349.                 register long        l = plines(lp);
  350.  
  351.                 do
  352.                 {
  353.                     curwin->w_topline = lp;
  354.                     if (--lp == 0)
  355.                         break;
  356.                     l += plines(lp);
  357.                 } while (l <= n);
  358.             }
  359.             if (nchar != 'z' && nchar != 'b')
  360.                 beginline(TRUE);
  361.             break;
  362.  
  363.           case Ctrl('S'):    /* ignore CTRL-S and CTRL-Q to avoid problems */
  364.           case Ctrl('Q'):    /* with terminals that use xon/xoff */
  365.               break;
  366.  
  367.           default:
  368.             CLEAROPBEEP;
  369.         }
  370.         updateScreen(VALID);
  371.         break;
  372.  
  373. /*
  374.  *      2: Control commands
  375.  */
  376.       case ':':
  377.         if (VIsual.lnum)
  378.             goto dooperator;
  379.         CHECKCLEAROP;
  380.         /*
  381.          * translate "count:" into ":.,.+(count - 1)"
  382.          */
  383.         if (Prenum)
  384.         {
  385.             stuffReadbuff((char_u *)".");
  386.             if (Prenum > 1)
  387.             {
  388.                 stuffReadbuff((char_u *)",.+");
  389.                 stuffnumReadbuff((long)Prenum - 1L);
  390.             }
  391.         }
  392.         docmdline(NULL);
  393.         modified = TRUE;
  394.         break;
  395.  
  396.       case K_HELP:
  397.         CHECKCLEAROP;
  398.         help();
  399.         break;
  400.  
  401.       case Ctrl('L'):
  402.         CHECKCLEAROP;
  403.         updateScreen(CLEAR);
  404.         break;
  405.  
  406.       case Ctrl('G'):
  407.         CHECKCLEAROP;
  408.         fileinfo(did_cd || Prenum);    /* print full name if count given or :cd used */
  409.         break;
  410.  
  411.       case K_CCIRCM:            /* CTRL-^, short for ":e #" */
  412.         CHECKCLEAROPQ;
  413.         (void)buflist_getfile((int)Prenum, (linenr_t)0, TRUE);
  414.         break;
  415.  
  416.       case 'Z':         /* write, if changed, and exit */
  417.         CHECKCLEAROPQ;
  418.         if (nchar != 'Z')
  419.         {
  420.             CLEAROPBEEP;
  421.             break;
  422.         }
  423.         stuffReadbuff((char_u *)":x\n");
  424.         break;
  425.  
  426.       case Ctrl(']'):            /* :ta to current identifier */
  427.         CHECKCLEAROPQ;
  428.       case '*':                 /* / to current identifier or string */
  429.       case '#':                 /* ? to current identifier or string */
  430.       case 'K':                    /* run program for current identifier */
  431.         {
  432.             register int     col;
  433.             register int    i;
  434.  
  435.             /*
  436.              * if i == 0: try to find an identifier
  437.              * if i == 1: try to find any string
  438.              */
  439.             ptr = ml_get(curwin->w_cursor.lnum);
  440.             for (i = 0;    i < 2; ++i)
  441.             {
  442.                 /*
  443.                  * skip to start of identifier/string
  444.                  */
  445.                 col = curwin->w_cursor.col;
  446.                 while (ptr[col] != NUL &&
  447.                             (i == 0 ? !isidchar(ptr[col]) : iswhite(ptr[col])))
  448.                     ++col;
  449.  
  450.                 /*
  451.                  * Back up to start of identifier/string. This doesn't match the
  452.                  * real vi but I like it a little better and it shouldn't bother
  453.                  * anyone.
  454.                  */
  455.                 while (col > 0 && (i == 0 ? isidchar(ptr[col - 1]) :
  456.                             (!iswhite(ptr[col - 1]) && !isidchar(ptr[col - 1]))))
  457.                     --col;
  458.  
  459.                 /*
  460.                  * if identifier found or not '*' or '#' command, stop searching
  461.                  */
  462.                 if (isidchar(ptr[col]) || (c != '*' && c != '#'))
  463.                     break;
  464.             }
  465.             /*
  466.              * did't find an identifier of string
  467.              */
  468.             if (ptr[col] == NUL || (!isidchar(ptr[col]) && i == 0))
  469.             {
  470.                 CLEAROPBEEP;
  471.                 break;
  472.             }
  473.  
  474.             if (Prenum)
  475.                 stuffnumReadbuff(Prenum);
  476.             switch (c)
  477.             {
  478.                 case '*':
  479.                     stuffReadbuff((char_u *)"/");
  480.                     goto sow;
  481.  
  482.                 case '#':
  483.                     stuffReadbuff((char_u *)"?");
  484. sow:                if (i == 0)
  485.                         stuffReadbuff((char_u *)"\\<");
  486.                     break;
  487.  
  488.                 case 'K':
  489.                     stuffReadbuff((char_u *)":! ");
  490.                     stuffReadbuff(p_kp);
  491.                     stuffReadbuff((char_u *)" ");
  492.                     break;
  493.                 default:
  494.                     stuffReadbuff((char_u *)":ta ");
  495.             }
  496.  
  497.             /*
  498.              * Now grab the chars in the identifier
  499.              */
  500.             while (i == 0 ? isidchar(ptr[col]) :
  501.                                 (ptr[col] != NUL && !iswhite(ptr[col])))
  502.             {
  503.                 stuffcharReadbuff(ptr[col]);
  504.                 ++col;
  505.             }
  506.             if ((c == '*' || c == '#') && i == 0)
  507.                 stuffReadbuff((char_u *)"\\>");
  508.             stuffReadbuff((char_u *)"\n");
  509.         }
  510.         break;
  511.  
  512.       case Ctrl('T'):        /* backwards in tag stack */
  513.             CHECKCLEAROPQ;
  514.               dotag((char_u *)"", 2, (int)Prenum1);
  515.             break;
  516.  
  517. /*
  518.  * Cursor motions
  519.  */
  520.       case 'G':
  521.         mtype = MLINE;
  522.         setpcmark();
  523.         if (Prenum == 0 || Prenum > curbuf->b_ml.ml_line_count)
  524.                 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
  525.         else
  526.                 curwin->w_cursor.lnum = Prenum;
  527.         beginline(TRUE);
  528.         break;
  529.  
  530.       case 'H':
  531.       case 'M':
  532.         if (c == 'M')
  533.                 n = (curwin->w_height - curwin->w_empty_rows) / 2;
  534.         else
  535.                 n = Prenum;
  536.         mtype = MLINE;
  537.         setpcmark();
  538.         curwin->w_cursor.lnum = curwin->w_topline;
  539.         while (n && onedown((long)1) == OK)
  540.                 --n;
  541.         beginline(TRUE);
  542.         break;
  543.  
  544.       case 'L':
  545.         mtype = MLINE;
  546.         setpcmark();
  547.         curwin->w_cursor.lnum = curwin->w_botline - 1;
  548.         for (n = Prenum; n && oneup((long)1) == OK; n--)
  549.                 ;
  550.         beginline(TRUE);
  551.         break;
  552.  
  553.       case 'l':
  554.       case K_RARROW:
  555.       case ' ':
  556.         mtype = MCHAR;
  557.         mincl = FALSE;
  558.         n = Prenum1;
  559.         while (n--)
  560.         {
  561.             if (oneright() == FAIL)
  562.             {
  563.                     /* space wraps to next line if 'whichwrap' bit 1 set */
  564.                     /* 'l' wraps to next line if 'whichwrap' bit 2 set */
  565.                     /* CURS_RIGHT wraps to next line if 'whichwrap' bit 3 set */
  566.                 if (((c == ' ' && (p_ww & 2)) ||
  567.                      (c == 'l' && (p_ww & 4)) ||
  568.                      (c == K_RARROW && (p_ww & 8))) &&
  569.                          curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
  570.                 {
  571.                     ++curwin->w_cursor.lnum;
  572.                     curwin->w_cursor.col = 0;
  573.                     curwin->w_set_curswant = TRUE;
  574.                     continue;
  575.                 }
  576.                 if (operator == NOP)
  577.                     beep();
  578.                 else
  579.                 {
  580.                     if (lineempty(curwin->w_cursor.lnum))
  581.                         CLEAROPBEEP;
  582.                     else
  583.                     {
  584.                         mincl = TRUE;
  585.                         if (n)
  586.                             beep();
  587.                     }
  588.                 }
  589.                 break;
  590.             }
  591.         }
  592.         break;
  593.  
  594.       case Ctrl('H'):
  595.       case 'h':
  596.       case K_LARROW:
  597.       case DEL:
  598.         mtype = MCHAR;
  599.         mincl = FALSE;
  600.         n = Prenum1;
  601.         while (n--)
  602.         {
  603.             if (oneleft() == FAIL)
  604.             {
  605.                     /* backspace and del wrap to previous line if 'whichwrap'
  606.                      *                                            bit 0 set */
  607.                     /* 'h' wraps to previous line if 'whichwrap' bit 2 set */
  608.                     /* CURS_LEFT wraps to previous line if 'whichwrap' bit 3 set */
  609.                 if ((((c == Ctrl('H') || c == DEL) && (p_ww & 1)) ||
  610.                      (c == 'h' && (p_ww & 4)) ||
  611.                      (c == K_LARROW && (p_ww & 8))) &&
  612.                             curwin->w_cursor.lnum > 1)
  613.                 {
  614.                     --(curwin->w_cursor.lnum);
  615.                     coladvance(MAXCOL);
  616.                     curwin->w_set_curswant = TRUE;
  617.                     continue;
  618.                 }
  619.                 else if (operator != DELETE && operator != CHANGE)
  620.                     beep();
  621.                 else if (Prenum1 == 1)
  622.                     CLEAROPBEEP;
  623.                 break;
  624.             }
  625.         }
  626.         break;
  627.  
  628.       case '-':
  629.         flag = TRUE;
  630.         /* FALLTHROUGH */
  631.  
  632.       case 'k':
  633.       case K_UARROW:
  634.       case Ctrl('P'):
  635.         mtype = MLINE;
  636.         if (oneup(Prenum1) == FAIL)
  637.             CLEAROPBEEP;
  638.         else if (flag)
  639.             beginline(TRUE);
  640.         break;
  641.  
  642.       case '+':
  643.       case CR:
  644.         flag = TRUE;
  645.         /* FALLTHROUGH */
  646.  
  647.       case 'j':
  648.       case K_DARROW:
  649.       case Ctrl('N'):
  650.       case NL:
  651.         mtype = MLINE;
  652.         if (onedown(Prenum1) == FAIL)
  653.             CLEAROPBEEP;
  654.         else if (flag)
  655.             beginline(TRUE);
  656.         break;
  657.  
  658.         /*
  659.          * This is a strange motion command that helps make operators more
  660.          * logical. It is actually implemented, but not documented in the
  661.          * real 'vi'. This motion command actually refers to "the current
  662.          * line". Commands like "dd" and "yy" are really an alternate form of
  663.          * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
  664.          * lines.
  665.          */
  666.       case '_':
  667. lineop:
  668.         mtype = MLINE;
  669.         if (onedown((long)(Prenum1 - 1)) == FAIL)
  670.             CLEAROPBEEP;
  671.         if (operator != YANK)            /* 'Y' does not move cursor */
  672.             beginline(TRUE);
  673.         break;
  674.  
  675.       case '|':
  676.         mtype = MCHAR;
  677.         mincl = TRUE;
  678.         beginline(FALSE);
  679.         if (Prenum > 0)
  680.             coladvance((colnr_t)(Prenum - 1));
  681.         curwin->w_curswant = (colnr_t)(Prenum - 1);
  682.             /* keep curswant at the column where we wanted to go, not where
  683.                 we ended; differs is line is too short */
  684.         curwin->w_set_curswant = FALSE;
  685.         break;
  686.  
  687.         /*
  688.          * Word Motions
  689.          */
  690.  
  691.       case 'B':
  692.         type = 1;
  693.         /* FALLTHROUGH */
  694.  
  695.       case 'b':
  696.       case K_SLARROW:
  697.         mtype = MCHAR;
  698.         mincl = FALSE;
  699.         curwin->w_set_curswant = TRUE;
  700.         if (bck_word(Prenum1, type))
  701.             CLEAROPBEEP;
  702.         break;
  703.  
  704.       case 'E':
  705.         type = 1;
  706.         /* FALLTHROUGH */
  707.  
  708.       case 'e':
  709.         mincl = TRUE;
  710.         goto dowrdcmd;
  711.  
  712.       case 'W':
  713.         type = 1;
  714.         /* FALLTHROUGH */
  715.  
  716.       case 'w':
  717.       case K_SRARROW:
  718.         mincl = FALSE;
  719.         flag = TRUE;
  720.         /*
  721.          * This is a little strange. To match what the real vi does, we
  722.          * effectively map 'cw' to 'ce', and 'cW' to 'cE', provided that we are
  723.          * not on a space or a TAB. This seems
  724.          * impolite at first, but it's really more what we mean when we say
  725.          * 'cw'.
  726.          * Another strangeness: When standing on the end of a word "ce" will
  727.          * change until the end of the next wordt, but "cw" will change only
  728.          * one character! This is done by setting type to 2.
  729.          */
  730.         if (operator == CHANGE && (n = gchar_cursor()) != ' ' && n != TAB &&
  731.                                                                 n != NUL)
  732.         {
  733.             mincl = TRUE;
  734.             flag = FALSE;
  735.             flag2 = TRUE;
  736.         }
  737.  
  738. dowrdcmd:
  739.         mtype = MCHAR;
  740.         curwin->w_set_curswant = TRUE;
  741.         if (flag)
  742.             n = fwd_word(Prenum1, type, operator != NOP);
  743.         else
  744.             n = end_word(Prenum1, type, flag2);
  745.         if (n)
  746.         {
  747.             CLEAROPBEEP;
  748.             break;
  749.         }
  750. #if 0
  751.         /*
  752.          * If we do a 'dw' for the last word in a line, we only delete the rest
  753.          * of the line, not joining the two lines, unless the current line is empty.
  754.          */
  755.         if (operator == DELETE && Prenum1 == 1 &&
  756.                 curbuf->b_startop.lnum != curwin->w_cursor.lnum && !lineempty(startop.lnum))
  757.         {
  758.                 curwin->w_cursor = curbuf->b_startop;
  759.                 while (oneright() == OK)
  760.                     ;
  761.                 mincl = TRUE;
  762.         }
  763. #endif
  764.         break;
  765.  
  766.       case '$':
  767.         mtype = MCHAR;
  768.         mincl = TRUE;
  769.         curwin->w_curswant = MAXCOL;                /* so we stay at the end */
  770.         if (onedown((long)(Prenum1 - 1)) == FAIL)
  771.         {
  772.             CLEAROPBEEP;
  773.             break;
  774.         }
  775.         break;
  776.  
  777.       case '^':
  778.         flag = TRUE;
  779.         /* FALLTHROUGH */
  780.  
  781.       case '0':
  782.         mtype = MCHAR;
  783.         mincl = FALSE;
  784.         beginline(flag);
  785.         break;
  786.  
  787. /*
  788.  * 4: Searches
  789.  */
  790.       case '?':
  791.       case '/':
  792.         if (!getcmdline(c, searchbuff))
  793.         {
  794.             CLEAROP;
  795.             break;
  796.         }
  797.         mtype = MCHAR;
  798.         mincl = FALSE;
  799.         curwin->w_set_curswant = TRUE;
  800.  
  801.         n = dosearch(c, searchbuff, FALSE, Prenum1, TRUE, TRUE);
  802.         if (n == 0)
  803.             CLEAROP;
  804.         else if (n == 2)
  805.             mtype = MLINE;
  806.         break;
  807.  
  808.       case 'N':
  809.         flag = 1;
  810.  
  811.       case 'n':
  812.         mtype = MCHAR;
  813.         mincl = FALSE;
  814.         curwin->w_set_curswant = TRUE;
  815.         if (!dosearch(0, NULL, flag, Prenum1, TRUE, TRUE))
  816.             CLEAROP;
  817.         break;
  818.  
  819.         /*
  820.          * Character searches
  821.          */
  822.       case 'T':
  823.         dir = BACKWARD;
  824.         /* FALLTHROUGH */
  825.  
  826.       case 't':
  827.         type = 1;
  828.         goto docsearch;
  829.  
  830.       case 'F':
  831.         dir = BACKWARD;
  832.         /* FALLTHROUGH */
  833.  
  834.       case 'f':
  835. docsearch:
  836.         mtype = MCHAR;
  837.         if (dir == BACKWARD)
  838.             mincl = FALSE;
  839.         else
  840.             mincl = TRUE;
  841.         curwin->w_set_curswant = TRUE;
  842.         if (!searchc(nchar, dir, type, Prenum1))
  843.             CLEAROPBEEP;
  844.         break;
  845.  
  846.       case ',':
  847.         flag = 1;
  848.         /* FALLTHROUGH */
  849.  
  850.       case ';':
  851.         dir = flag;
  852.         goto docsearch;        /* nchar == NUL, thus repeat previous search */
  853.  
  854.         /*
  855.          * section or C function searches
  856.          */
  857.       case '[':
  858.         dir = BACKWARD;
  859.         /* FALLTHROUGH */
  860.  
  861.       case ']':
  862.         mtype = MCHAR;
  863.         mincl = FALSE;
  864.  
  865.         /*
  866.          * "[f" or "]f" : Edit file under the cursor (same as "gf")
  867.          */
  868.         if ((c == ']' || c == '[') && nchar == 'f')
  869.             goto gotofile;
  870.  
  871.         /*
  872.          * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
  873.          */
  874.         if ((c == '[' && (nchar == '{' || nchar == '(')) ||
  875.            ((c == ']' && (nchar == '}' || nchar == ')'))))
  876.         {
  877.             FPOS old_pos;
  878.  
  879.             old_pos = curwin->w_cursor;
  880.             while (Prenum1--)
  881.             {
  882.                 if ((pos = showmatch(nchar)) == NULL)
  883.                 {
  884.                     CLEAROPBEEP;
  885.                     break;
  886.                 }
  887.                 curwin->w_cursor = *pos;
  888.             }
  889.             curwin->w_cursor = old_pos;
  890.             if (pos != NULL)
  891.             {
  892.                 setpcmark();
  893.                 curwin->w_cursor = *pos;
  894.                 curwin->w_set_curswant = TRUE;
  895.             }
  896.             break;
  897.         }
  898.  
  899.         /*
  900.          * "[[", "[]", "]]" and "][": move to start or end of function
  901.          */
  902.         if (nchar == '[' || nchar == ']')
  903.         {
  904.             if (nchar == c)                /* "]]" or "[[" */
  905.                 flag = '{';
  906.             else
  907.                 flag = '}';                /* "][" or "[]" */
  908.  
  909.             curwin->w_set_curswant = TRUE;
  910.             /*
  911.              * Imitate strange vi behaviour: When using "]]" with an operator we
  912.              * also stop at '}'.
  913.              */
  914.             if (!findpar(dir, Prenum1, flag,
  915.                             (operator != NOP && dir == FORWARD && flag == '{')))
  916.                 CLEAROPBEEP;
  917.             break;
  918.         }
  919.  
  920.         /*
  921.          * "[p" and "]p": put with indent adjustment
  922.          */
  923.         if (nchar == 'p')
  924.         {
  925.             doput((c == ']') ? FORWARD : BACKWARD, Prenum1, TRUE);
  926.             modified = TRUE;
  927.             break;
  928.         }
  929.  
  930.         /*
  931.          * end of '[' and ']': not a valid nchar
  932.          */
  933.         CLEAROPBEEP;
  934.         break;
  935.  
  936.       case '%':
  937.         mincl = TRUE;
  938.         if (Prenum)        /* {cnt}% : goto {cnt} percentage in file */
  939.         {
  940.             if (Prenum > 100)
  941.                 CLEAROPBEEP;
  942.             else
  943.             {
  944.                 mtype = MLINE;
  945.                 setpcmark();
  946.                         /* round up, so CTRL-G will give same value */
  947.                 curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count * Prenum + 99) / 100;
  948.                 beginline(TRUE);
  949.             }
  950.         }
  951.         else            /* % : go to matching paren */
  952.         {
  953.             mtype = MCHAR;
  954.             if ((pos = showmatch(NUL)) == NULL)
  955.                 CLEAROPBEEP;
  956.             else
  957.             {
  958.                 setpcmark();
  959.                 curwin->w_cursor = *pos;
  960.                 curwin->w_set_curswant = TRUE;
  961.             }
  962.         }
  963.         break;
  964.  
  965.       case '(':
  966.         dir = BACKWARD;
  967.         /* FALLTHROUGH */
  968.  
  969.       case ')':
  970.         mtype = MCHAR;
  971.         if (c == ')')
  972.             mincl = FALSE;
  973.         else
  974.             mincl = TRUE;
  975.         curwin->w_set_curswant = TRUE;
  976.  
  977.         if (!findsent(dir, Prenum1))
  978.             CLEAROPBEEP;
  979.         break;
  980.  
  981.       case '{':
  982.         dir = BACKWARD;
  983.         /* FALLTHROUGH */
  984.  
  985.       case '}':
  986.         mtype = MCHAR;
  987.         mincl = FALSE;
  988.         curwin->w_set_curswant = TRUE;
  989.         if (!findpar(dir, Prenum1, NUL, FALSE))
  990.             CLEAROPBEEP;
  991.         break;
  992.  
  993. /*
  994.  * 5: Edits
  995.  */
  996.       case '.':
  997.         CHECKCLEAROPQ;
  998.         if (start_redo(Prenum) == FAIL)
  999.             CLEAROPBEEP;
  1000.         modified = TRUE;
  1001.         break;
  1002.  
  1003.       case 'u':
  1004.         if (VIsual.lnum)
  1005.             goto dooperator;
  1006.       case K_UNDO:
  1007.         CHECKCLEAROPQ;
  1008.         u_undo((int)Prenum1);
  1009.         curwin->w_set_curswant = TRUE;
  1010.         modified = TRUE;
  1011.         break;
  1012.  
  1013.       case Ctrl('R'):
  1014.         CHECKCLEAROPQ;
  1015.           u_redo((int)Prenum1);
  1016.         curwin->w_set_curswant = TRUE;
  1017.         modified = TRUE;
  1018.         break;
  1019.  
  1020.       case 'U':
  1021.         if (VIsual.lnum)
  1022.             goto dooperator;
  1023.         CHECKCLEAROPQ;
  1024.         u_undoline();
  1025.         curwin->w_set_curswant = TRUE;
  1026.         modified = TRUE;
  1027.         break;
  1028.  
  1029.       case 'r':
  1030.         if (VIsual.lnum)
  1031.         {
  1032.             c = 'c';
  1033.             goto dooperator;
  1034.         }
  1035.         CHECKCLEAROPQ;
  1036.         ptr = ml_get_cursor();
  1037.         if (STRLEN(ptr) < (unsigned)Prenum1)    /* not enough characters to replace */
  1038.         {
  1039.             CLEAROPBEEP;
  1040.             break;
  1041.         }
  1042.         /*
  1043.          * Replacing with a line break or tab is done by edit(), because it
  1044.          * is complicated.
  1045.          * Other characters are done below to avoid problems with things like
  1046.          * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
  1047.          */
  1048.         if (nchar == '\r' || nchar == '\n' || nchar == '\t')
  1049.         {
  1050.             prep_redo(Prenum1, 'r', nchar, NUL);
  1051.             stuffnumReadbuff(Prenum1);
  1052.             stuffcharReadbuff('R');
  1053.             stuffcharReadbuff(nchar);
  1054.             stuffcharReadbuff(ESC);
  1055.             break;
  1056.         }
  1057.  
  1058.         if (nchar == Ctrl('V'))                /* get another character */
  1059.         {
  1060.             c = Ctrl('V');
  1061.             nchar = get_literal(&type);
  1062.             if (type)                        /* typeahead */
  1063.                 stuffcharReadbuff(type);
  1064.         }
  1065.         else
  1066.             c = NUL;
  1067.         prep_redo(Prenum1, 'r', c, nchar);
  1068.         if (!u_save_cursor())                /* save line for undo */
  1069.             break;
  1070.             /*
  1071.              * Get ptr again, because u_save will have released the line.
  1072.              * At the same time we let know that the line will be changed.
  1073.              */
  1074.         ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE) + curwin->w_cursor.col;
  1075.         curwin->w_cursor.col += Prenum1 - 1;
  1076.         while (Prenum1--)                    /* replace the characters */
  1077.             *ptr++ = nchar;
  1078.         curwin->w_set_curswant = TRUE;
  1079.         CHANGED;
  1080.         updateline();
  1081.         modified = TRUE;
  1082.         break;
  1083.  
  1084.       case 'J':
  1085.         if (VIsual.lnum)        /* join the visual lines */
  1086.         {
  1087.             if (curwin->w_cursor.lnum > VIsual.lnum)
  1088.             {
  1089.                 Prenum = curwin->w_cursor.lnum - VIsual.lnum + 1;
  1090.                 curwin->w_cursor.lnum = VIsual.lnum;
  1091.             }
  1092.             else
  1093.                 Prenum = VIsual.lnum - curwin->w_cursor.lnum + 1;
  1094.             VIsual.lnum = 0;
  1095.         }
  1096.         CHECKCLEAROP;
  1097.         if (Prenum <= 1)
  1098.             Prenum = 2;             /* default for join is two lines! */
  1099.         if (curwin->w_cursor.lnum + Prenum - 1 > curbuf->b_ml.ml_line_count)    /* beyond last line */
  1100.         {
  1101.             CLEAROPBEEP;
  1102.             break;
  1103.         }
  1104.  
  1105.         prep_redo(Prenum, 'J', NUL, NUL);
  1106.         dodojoin(Prenum, TRUE, TRUE);
  1107.         modified = TRUE;
  1108.         break;
  1109.  
  1110.       case 'P':
  1111.         dir = BACKWARD;
  1112.         /* FALLTHROUGH */
  1113.  
  1114.       case 'p':
  1115.         CHECKCLEAROPQ;
  1116.         prep_redo(Prenum, c, NUL, NUL);
  1117.         doput(dir, Prenum1, FALSE);
  1118.         modified = TRUE;
  1119.         break;
  1120.  
  1121.       case Ctrl('A'):            /* add to number */
  1122.       case Ctrl('X'):            /* subtract from number */
  1123.         CHECKCLEAROPQ;
  1124.         if (doaddsub((int)c, Prenum1) == OK)
  1125.             prep_redo(Prenum1, c, NUL, NUL);
  1126.         modified = TRUE;
  1127.         break;
  1128.  
  1129. /*
  1130.  * 6: Inserts
  1131.  */
  1132.       case 'A':
  1133.         curwin->w_set_curswant = TRUE;
  1134.         while (oneright() == OK)
  1135.                 ;
  1136.         /* FALLTHROUGH */
  1137.  
  1138.       case 'a':
  1139.         CHECKCLEAROPQ;
  1140.         /* Works just like an 'i'nsert on the next character. */
  1141.         if (u_save_cursor())
  1142.         {
  1143.             if (!lineempty(curwin->w_cursor.lnum))
  1144.                 inc_cursor();
  1145.             startinsert(c, FALSE, Prenum1);
  1146.             modified = TRUE;
  1147.             command_busy = TRUE;
  1148.         }
  1149.         break;
  1150.  
  1151.       case 'I':
  1152.         beginline(TRUE);
  1153.         /* FALLTHROUGH */
  1154.  
  1155.       case 'i':
  1156.         CHECKCLEAROPQ;
  1157.         if (u_save_cursor())
  1158.         {
  1159.             startinsert(c, FALSE, Prenum1);
  1160.             modified = TRUE;
  1161.             command_busy = TRUE;
  1162.         }
  1163.         break;
  1164.  
  1165.       case 'o':
  1166.           if (VIsual.lnum)    /* switch start and end of visual */
  1167.         {
  1168.             Prenum = VIsual.lnum;
  1169.             VIsual.lnum = curwin->w_cursor.lnum;
  1170.             curwin->w_cursor.lnum = Prenum;
  1171.             if (VIsual.col != VISUALLINE)
  1172.             {
  1173.                 n = VIsual.col;
  1174.                 VIsual.col = curwin->w_cursor.col;
  1175.                 curwin->w_cursor.col = (int)n;
  1176.                 curwin->w_set_curswant = TRUE;
  1177.             }
  1178.             break;
  1179.         }
  1180.         CHECKCLEAROP;
  1181.         if (u_save(curwin->w_cursor.lnum, (linenr_t)(curwin->w_cursor.lnum + 1)) &&
  1182.                             Opencmd(FORWARD, TRUE, TRUE))
  1183.         {
  1184.             startinsert('o', TRUE, Prenum1);
  1185.             modified = TRUE;
  1186.             command_busy = TRUE;
  1187.         }
  1188.         break;
  1189.  
  1190.       case 'O':
  1191.         CHECKCLEAROPQ;
  1192.         if (u_save((linenr_t)(curwin->w_cursor.lnum - 1), curwin->w_cursor.lnum) && Opencmd(BACKWARD, TRUE, TRUE))
  1193.         {
  1194.             startinsert('O', TRUE, Prenum1);
  1195.             modified = TRUE;
  1196.             command_busy = TRUE;
  1197.         }
  1198.         break;
  1199.  
  1200.       case 'R':
  1201.         if (VIsual.lnum)
  1202.         {
  1203.             c = 'c';
  1204.             VIsual.col = VISUALLINE;
  1205.             goto dooperator;
  1206.         }
  1207.         CHECKCLEAROPQ;
  1208.         if (u_save_cursor())
  1209.         {
  1210.             startinsert('R', FALSE, Prenum1);
  1211.             modified = TRUE;
  1212.             command_busy = TRUE;
  1213.         }
  1214.         break;
  1215.  
  1216. /*
  1217.  * 7: Operators
  1218.  */
  1219.       case '~':         /* swap case */
  1220.       /*
  1221.        * if tilde is not an operator and Visual is off: swap case
  1222.        * of a single character
  1223.        */
  1224.         if (!p_to && !VIsual.lnum)
  1225.         {
  1226.             CHECKCLEAROPQ;
  1227.             if (lineempty(curwin->w_cursor.lnum))
  1228.             {
  1229.                 CLEAROPBEEP;
  1230.                 break;
  1231.             }
  1232.             prep_redo(Prenum, '~', NUL, NUL);
  1233.  
  1234.             if (!u_save_cursor())
  1235.                 break;
  1236.  
  1237.             for (; Prenum1 > 0; --Prenum1)
  1238.             {
  1239.                 if (gchar_cursor() == NUL)
  1240.                     break;
  1241.                 swapchar(&curwin->w_cursor);
  1242.                 inc_cursor();
  1243.             }
  1244.  
  1245.             curwin->w_set_curswant = TRUE;
  1246.             CHANGED;
  1247.             updateline();
  1248.             modified = TRUE;
  1249.             break;
  1250.         }
  1251.         /*FALLTHROUGH*/
  1252.  
  1253.       case 'd':
  1254.       case 'c':
  1255.       case 'y':
  1256.       case '>':
  1257.       case '<':
  1258.       case '!':
  1259.       case '=':
  1260.       case 'Q':
  1261. dooperator:
  1262.         n = STRCHR(opchars, c) - opchars + 1;
  1263.         if (n == operator)        /* double operator works on lines */
  1264.             goto lineop;
  1265.         CHECKCLEAROP;
  1266.         if (Prenum != 0)
  1267.             opnum = Prenum;
  1268.         curbuf->b_startop = curwin->w_cursor;
  1269.         operator = (int)n;
  1270.         break;
  1271.  
  1272. /*
  1273.  * 8: Abbreviations
  1274.  */
  1275.  
  1276.      /* when Visual the next commands are operators */
  1277.       case 'S':
  1278.       case 'Y':
  1279.       case 'D':
  1280.       case 'C':
  1281.       case 'x':
  1282.       case 'X':
  1283.       case 's':
  1284.           if (VIsual.lnum)
  1285.         {
  1286.             static char_u trans[] = "ScYyDdCcxdXdsc";
  1287.  
  1288.             if (isupper(c) && !Visual_block)        /* uppercase means linewise */
  1289.                 VIsual.col = VISUALLINE;
  1290.             c = *(STRCHR(trans, c) + 1);
  1291.             goto dooperator;
  1292.         }
  1293.  
  1294.       case '&':
  1295.         CHECKCLEAROPQ;
  1296.         if (Prenum)
  1297.             stuffnumReadbuff(Prenum);
  1298.  
  1299.         if (c == 'Y' && p_ye)
  1300.             c = 'Z';
  1301.         {
  1302.                 static char_u *(ar[9]) = {(char_u *)"dl", (char_u *)"dh", (char_u *)"d$", (char_u *)"c$", (char_u *)"cl", (char_u *)"cc", (char_u *)"yy", (char_u *)"y$", (char_u *)":s\r"};
  1303.                 static char_u *str = (char_u *)"xXDCsSYZ&";
  1304.  
  1305.                 stuffReadbuff(ar[(int)(STRCHR(str, c) - str)]);
  1306.         }
  1307.         break;
  1308.  
  1309. /*
  1310.  * 9: Marks
  1311.  */
  1312.  
  1313.       case 'm':
  1314.         CHECKCLEAROP;
  1315.         if (setmark(nchar) == FAIL)
  1316.             CLEAROPBEEP;
  1317.         break;
  1318.  
  1319.       case '\'':
  1320.         flag = TRUE;
  1321.         /* FALLTHROUGH */
  1322.  
  1323.       case '`':
  1324.         pos = getmark(nchar, (operator == NOP));
  1325.         if (pos == (FPOS *)-1)    /* jumped to other file */
  1326.         {
  1327.             if (flag)
  1328.                 beginline(TRUE);
  1329.             break;
  1330.         }
  1331.  
  1332.         if (pos != NULL)
  1333.             setpcmark();
  1334.  
  1335. cursormark:
  1336.         if (pos == NULL || pos->lnum == 0)
  1337.             CLEAROPBEEP;
  1338.         else
  1339.         {
  1340.             curwin->w_cursor = *pos;
  1341.             if (flag)
  1342.                 beginline(TRUE);
  1343.         }
  1344.         mtype = flag ? MLINE : MCHAR;
  1345.         mincl = FALSE;        /* ignored if not MCHAR */
  1346.         curwin->w_set_curswant = TRUE;
  1347.         break;
  1348.  
  1349.     case Ctrl('O'):            /* goto older pcmark */
  1350.         Prenum1 = -Prenum1;
  1351.         /* FALLTHROUGH */
  1352.  
  1353.     case Ctrl('I'):            /* goto newer pcmark */
  1354.         CHECKCLEAROPQ;
  1355.         pos = movemark((int)Prenum1);
  1356.         if (pos == (FPOS *)-1)    /* jump to other file */
  1357.         {
  1358.             curwin->w_set_curswant = TRUE;
  1359.             break;
  1360.         }
  1361.         goto cursormark;
  1362.  
  1363. /*
  1364.  * 10. Buffer setting
  1365.  */
  1366.       case '"':
  1367.         CHECKCLEAROP;
  1368.         if (nchar != NUL && is_yank_buffer(nchar, FALSE))
  1369.         {
  1370.             yankbuffer = nchar;
  1371.             opnum = Prenum;        /* remember count before '"' */
  1372.         }
  1373.         else
  1374.             CLEAROPBEEP;
  1375.         break;
  1376.  
  1377. /*
  1378.  * 11. Visual
  1379.  */
  1380.        case 'v':
  1381.       case 'V':
  1382.       case Ctrl('V'):
  1383.         CHECKCLEAROP;
  1384.         Visual_block = FALSE;
  1385.  
  1386.             /* stop Visual */
  1387.         if (VIsual.lnum)
  1388.         {
  1389.             VIsual.lnum = 0;
  1390.             updateScreen(NOT_VALID);        /* delete the inversion */
  1391.         }
  1392.             /* start Visual */
  1393.         else
  1394.         {
  1395.             if (!didwarn && set_highlight('v') == FAIL)/* cannot highlight */
  1396.             {
  1397.                 EMSG("Warning: terminal cannot highlight");
  1398.                 didwarn = TRUE;
  1399.             }
  1400.             if (Prenum)                        /* use previously selected part */
  1401.             {
  1402.                 if (!resel_Visual_type)        /* there is none */
  1403.                 {
  1404.                     beep();
  1405.                     break;
  1406.                 }
  1407.                 VIsual = curwin->w_cursor;
  1408.                 if (resel_Visual_nlines > 1)
  1409.                     curwin->w_cursor.lnum += resel_Visual_nlines * Prenum - 1;
  1410.                 switch (resel_Visual_type)
  1411.                 {
  1412.                 case 'V':    VIsual.col = VISUALLINE;
  1413.                             break;
  1414.  
  1415.                 case Ctrl('V'):
  1416.                             Visual_block = TRUE;
  1417.                             break;
  1418.  
  1419.                 case 'v':        
  1420.                             if (resel_Visual_nlines <= 1)
  1421.                                 curwin->w_cursor.col += resel_Visual_col * Prenum - 1;
  1422.                             else
  1423.                                 curwin->w_cursor.col = resel_Visual_col;
  1424.                             break;
  1425.                 }
  1426.                 if (resel_Visual_col == MAXCOL)
  1427.                 {
  1428.                     curwin->w_curswant = MAXCOL;
  1429.                     coladvance(MAXCOL);
  1430.                 }
  1431.                 else if (Visual_block)
  1432.                     coladvance((colnr_t)(curwin->w_virtcol + resel_Visual_col * Prenum - 1));
  1433.                 curs_columns(TRUE);            /* recompute w_virtcol */
  1434.                 updateScreen(NOT_VALID);    /* show the inversion */
  1435.             }
  1436.             else
  1437.             {
  1438.                 VIsual = curwin->w_cursor;
  1439.                 if (c == 'V')                /* linewise */
  1440.                     VIsual.col = VISUALLINE;
  1441.                 else if (c == Ctrl('V'))    /* blockwise */
  1442.                     Visual_block = TRUE;
  1443.                 updateline();                /* start the inversion */
  1444.             }
  1445.         }
  1446.         break;
  1447.  
  1448. /*
  1449.  * 12. Suspend
  1450.  */
  1451.  
  1452.      case Ctrl('Z'):
  1453.         CLEAROP;
  1454.         VIsual.lnum = 0;                    /* stop Visual */
  1455.         stuffReadbuff((char_u *)":st\r");    /* with autowrite */
  1456.         break;
  1457.  
  1458. /*
  1459.  * 13. Window commands
  1460.  */
  1461.  
  1462.      case Ctrl('W'):
  1463.         CHECKCLEAROP;
  1464.         do_window(nchar, Prenum);            /* everything is in window.c */
  1465.         break;
  1466.  
  1467. /*
  1468.  *   14. extended commands (starting with 'g')
  1469.  */
  1470.      case 'g':
  1471.         switch (nchar)
  1472.         {
  1473.                         /*
  1474.                          * "gf": goto file, edit file under cursor
  1475.                          * "]f" and "[f": can also be used.
  1476.                          */
  1477.             case 'f':
  1478. gotofile:
  1479.                         ptr = file_name_at_cursor();
  1480.                             /* do autowrite if necessary */
  1481.                         if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
  1482.                             autowrite(curbuf);
  1483.                         if (ptr != NULL)
  1484.                         {
  1485.                             setpcmark();
  1486.                             stuffReadbuff((char_u *) ":e ");
  1487.                             stuffReadbuff(ptr);
  1488.                             stuffReadbuff((char_u *) "\n");
  1489.                             free(ptr);
  1490.                         }
  1491.                         else
  1492.                             CLEAROPBEEP;
  1493.                         break;
  1494.  
  1495.                         /*
  1496.                          * "gs": goto sleep
  1497.                          */
  1498.             case 's':    while (Prenum1-- && !got_int)
  1499.                         {
  1500.                             sleep(1);
  1501.                             breakcheck();
  1502.                         }
  1503.                         break;
  1504.  
  1505.             default:    CLEAROPBEEP;
  1506.                         break;
  1507.         }
  1508.         break;
  1509.  
  1510. /*
  1511.  * The end
  1512.  */
  1513.       case ESC:
  1514.         if (VIsual.lnum)
  1515.         {
  1516.             VIsual.lnum = 0;            /* stop Visual */
  1517.             updateScreen(NOT_VALID);
  1518.             CLEAROP;                    /* don't beep */
  1519.             break;
  1520.         }
  1521.         /* Don't drop through and beep if we are canceling a command: */
  1522.         else if (operator != NOP || opnum || Prenum || yankbuffer)
  1523.         {
  1524.             CLEAROP;                    /* don't beep */
  1525.             break;
  1526.         }
  1527.         /* FALLTHROUGH */
  1528.  
  1529.       default:                    /* not a known command */
  1530.         CLEAROPBEEP;
  1531.         break;
  1532.  
  1533.     }    /* end of switch on command character */
  1534.  
  1535. /*
  1536.  * if we didn't start or finish an operator, reset yankbuffer, unless we
  1537.  * need it later.
  1538.  */
  1539.     if (!finish_op && !operator && strchr("\"DCYSsXx.", c) == NULL)
  1540.         yankbuffer = 0;
  1541.  
  1542.     /*
  1543.      * If an operation is pending, handle it...
  1544.      */
  1545.     if ((VIsual.lnum || finish_op) && operator != NOP)
  1546.     {
  1547.         if (operator != YANK && !VIsual.lnum)        /* can't redo yank */
  1548.         {
  1549.             prep_redo(Prenum, opchars[operator - 1], c, nchar);
  1550.             if (c == '/' || c == '?')                /* was a search */
  1551.             {
  1552.                 AppendToRedobuff(searchbuff);
  1553.                 AppendToRedobuff(NL_STR);
  1554.             }
  1555.         }
  1556.  
  1557.         if (redo_Visual_busy)
  1558.         {
  1559.             curbuf->b_startop = curwin->w_cursor;
  1560.             curwin->w_cursor.lnum += redo_Visual_nlines - 1;
  1561.             switch (redo_Visual_type)
  1562.             {
  1563.             case 'V':    VIsual.col = VISUALLINE;
  1564.                         break;
  1565.  
  1566.             case Ctrl('V'):
  1567.                         Visual_block = TRUE;
  1568.                         break;
  1569.  
  1570.             case 'v':        
  1571.                         if (redo_Visual_nlines <= 1)
  1572.                             curwin->w_cursor.col += redo_Visual_col - 1;
  1573.                         else
  1574.                             curwin->w_cursor.col = redo_Visual_col;
  1575.                         break;
  1576.             }
  1577.             if (redo_Visual_col == MAXCOL)
  1578.             {
  1579.                 curwin->w_curswant = MAXCOL;
  1580.                 coladvance(MAXCOL);
  1581.             }
  1582.             Prenum = redo_Visual_Prenum;
  1583.             if (Prenum == 0)
  1584.                 Prenum1 = 1L;
  1585.             else
  1586.                 Prenum1 = Prenum;
  1587.         }
  1588.         else if (VIsual.lnum)
  1589.             curbuf->b_startop = VIsual;
  1590.  
  1591.         if (lt(curbuf->b_startop, curwin->w_cursor))
  1592.         {
  1593.             curbuf->b_endop = curwin->w_cursor;
  1594.             curwin->w_cursor = curbuf->b_startop;
  1595.         }
  1596.         else
  1597.         {
  1598.             curbuf->b_endop = curbuf->b_startop;
  1599.             curbuf->b_startop = curwin->w_cursor;
  1600.         }
  1601.         nlines = curbuf->b_endop.lnum - curbuf->b_startop.lnum + 1;
  1602.  
  1603.         if (VIsual.lnum || redo_Visual_busy)
  1604.         {
  1605.             if (Visual_block)                /* block mode */
  1606.             {
  1607.                 startvcol = getvcol(curwin, &(curbuf->b_startop), 2);
  1608.                 n = getvcol(curwin, &(curbuf->b_endop), 2);
  1609.                 if (n < startvcol)
  1610.                     startvcol = (colnr_t)n;
  1611.  
  1612.             /* if '$' was used, get endvcol from longest line */
  1613.                 if (curwin->w_curswant == MAXCOL)
  1614.                 {
  1615.                     curwin->w_cursor.col = MAXCOL;
  1616.                     endvcol = 0;
  1617.                     for (curwin->w_cursor.lnum = curbuf->b_startop.lnum; curwin->w_cursor.lnum <= curbuf->b_endop.lnum; ++curwin->w_cursor.lnum)
  1618.                         if ((n = getvcol(curwin, &curwin->w_cursor, 3)) > endvcol)
  1619.                             endvcol = (colnr_t)n;
  1620.                     curwin->w_cursor = curbuf->b_startop;
  1621.                 }
  1622.                 else if (redo_Visual_busy)
  1623.                     endvcol = startvcol + redo_Visual_col - 1;
  1624.                 else
  1625.                 {
  1626.                     endvcol = getvcol(curwin, &(curbuf->b_startop), 3);
  1627.                     n = getvcol(curwin, &(curbuf->b_endop), 3);
  1628.                     if (n > endvcol)
  1629.                         endvcol = (colnr_t)n;
  1630.                 }
  1631.                 coladvance(startvcol);
  1632.             }
  1633.  
  1634.     /*
  1635.      * prepare to reselect and redo Visual: this is based on the size
  1636.      * of the Visual text
  1637.      */
  1638.             if (Visual_block)
  1639.                 resel_Visual_type = Ctrl('V');
  1640.             else if (VIsual.col == VISUALLINE)
  1641.                 resel_Visual_type = 'V';
  1642.             else
  1643.                 resel_Visual_type = 'v';
  1644.             if (curwin->w_curswant == MAXCOL)
  1645.                 resel_Visual_col = MAXCOL;
  1646.             else if (Visual_block)
  1647.                 resel_Visual_col = endvcol - startvcol + 1;
  1648.             else if (nlines > 1)
  1649.                 resel_Visual_col = curbuf->b_endop.col;
  1650.             else
  1651.                 resel_Visual_col = curbuf->b_endop.col - curbuf->b_startop.col + 1;
  1652.             resel_Visual_nlines = nlines;
  1653.             if (operator != YANK && operator != COLON)    /* can't redo yank and : */
  1654.             {
  1655.                 prep_redo(0L, 'v', opchars[operator - 1], NUL);
  1656.                 redo_Visual_type = resel_Visual_type;
  1657.                 redo_Visual_col = resel_Visual_col;
  1658.                 redo_Visual_nlines = resel_Visual_nlines;
  1659.                 redo_Visual_Prenum = Prenum;
  1660.             }
  1661.  
  1662.             /*
  1663.              * Mincl defaults to TRUE.
  1664.              * If endop is on a NUL (empty line) mincl becomes FALSE
  1665.              * This makes "d}P" and "v}dP" work the same.
  1666.              */
  1667.             mincl = TRUE;
  1668.             if (VIsual.col == VISUALLINE)
  1669.                 mtype = MLINE;
  1670.             else
  1671.             {
  1672.                 mtype = MCHAR;
  1673.                 if (*ml_get_pos(&(curbuf->b_endop)) == NUL)
  1674.                     mincl = FALSE;
  1675.             }
  1676.  
  1677.             redo_Visual_busy = FALSE;
  1678.             /*
  1679.              * Switch Visual off now, so screen updating does
  1680.              * not show inverted text when the screen is redrawn.
  1681.              * With YANK and sometimes with COLON and FILTER there is no screen
  1682.              * redraw, so it is done here to remove the inverted part.
  1683.              */
  1684.             VIsual.lnum = 0;
  1685.             if (operator == YANK || operator == COLON || operator == FILTER)
  1686.                 updateScreen(NOT_VALID);
  1687.         }
  1688.         else if (operator == LSHIFT || operator == RSHIFT)
  1689.             Prenum1 = 1L;        /* if not visual mode: shift one indent */
  1690.  
  1691.         curwin->w_set_curswant = 1;
  1692.  
  1693.             /* no_op is set when start and end are the same */
  1694.         no_op = (mtype == MCHAR && !mincl && equal(curbuf->b_startop, curbuf->b_endop));
  1695.  
  1696.     /*
  1697.      * If the end of an operator is in column one while mtype is MCHAR and mincl
  1698.      * is FALSE, we put endop after the last character in the previous line.
  1699.      * If startop is on or before the first non-blank in the line, the operator
  1700.      * becomes linewise (strange, but that's the way vi does it).
  1701.      */
  1702.         if (mtype == MCHAR && mincl == FALSE && curbuf->b_endop.col == 0 && nlines > 1)
  1703.         {
  1704.             --nlines;
  1705.             --curbuf->b_endop.lnum;
  1706.             if (inindent())
  1707.                 mtype = MLINE;
  1708.             else
  1709.             {
  1710.                 curbuf->b_endop.col = STRLEN(ml_get(curbuf->b_endop.lnum));
  1711.                 if (curbuf->b_endop.col)
  1712.                 {
  1713.                     --curbuf->b_endop.col;
  1714.                     mincl = TRUE;
  1715.                 }
  1716.             }
  1717.         }
  1718.         switch (operator)
  1719.         {
  1720.           case LSHIFT:
  1721.           case RSHIFT:
  1722.             doshift(operator, TRUE, (int)Prenum1);
  1723.             modified = TRUE;
  1724.             break;
  1725.  
  1726.           case DELETE:
  1727.             if (!no_op)
  1728.             {
  1729.                 dodelete();
  1730.                 modified = TRUE;
  1731.             }
  1732.             break;
  1733.  
  1734.           case YANK:
  1735.             if (!no_op)
  1736.                 (void)doyank(FALSE);
  1737.             break;
  1738.  
  1739.           case CHANGE:
  1740.             dochange();
  1741.             modified = TRUE;
  1742.             command_busy = TRUE;
  1743.             break;
  1744.  
  1745.           case FILTER:
  1746.             bangredo = TRUE;            /* dobang() will put cmd in redo buffer */
  1747.  
  1748.           case INDENT:
  1749.           case COLON:
  1750. dofilter:
  1751.             sprintf((char *)IObuff, ":%ld,%ld", (long)curbuf->b_startop.lnum, (long)curbuf->b_endop.lnum);
  1752.             stuffReadbuff(IObuff);
  1753.             if (operator != COLON)
  1754.                 stuffReadbuff((char_u *)"!");
  1755.             if (operator == INDENT)
  1756.             {
  1757.                 stuffReadbuff(p_ep);
  1758.                 stuffReadbuff((char_u *)"\n");
  1759.             }
  1760.             else if (operator == FORMAT)
  1761.             {
  1762.                 stuffReadbuff(p_fp);
  1763.                 stuffReadbuff((char_u *)"\n");
  1764.             }
  1765.                 /*    docmdline() does the rest */
  1766.             break;
  1767.  
  1768.           case TILDE:
  1769.           case UPPER:
  1770.           case LOWER:
  1771.             if (!no_op)
  1772.             {
  1773.                 dotilde();
  1774.                 modified = TRUE;
  1775.             }
  1776.             break;
  1777.  
  1778.           case FORMAT:
  1779.             if (*p_fp != NUL)
  1780.                 goto dofilter;        /* use external command */
  1781.             doformat();                /* use internal function */
  1782.             modified = TRUE;
  1783.             break;
  1784.  
  1785.           default:
  1786.             CLEAROPBEEP;
  1787.         }
  1788.         operator = NOP;
  1789.         Visual_block = FALSE;
  1790.         yankbuffer = 0;
  1791.     }
  1792.  
  1793. normal_end:
  1794.     premsg(-1, NUL);
  1795.  
  1796.     if (restart_edit && operator == NOP && VIsual.lnum == 0 && !command_busy && stuff_empty() && yankbuffer == 0)
  1797.     {
  1798.         startinsert(restart_edit, FALSE, 1L);
  1799.         modified = TRUE;
  1800.     }
  1801.  
  1802.     checkpcmark();            /* check if we moved since setting pcmark */
  1803.  
  1804. /*
  1805.  * TEMPORARY: update the other windows for the current buffer if modified
  1806.  */
  1807.     if (modified)
  1808.     {
  1809.         WIN        *wp;
  1810.  
  1811.         for (wp = firstwin; wp; wp = wp->w_next)
  1812.             if (wp != curwin && wp->w_buffer == curbuf)
  1813.             {
  1814.                 cursor_off();
  1815.                 wp->w_redr_type = NOT_VALID;
  1816.                 win_update(wp);
  1817.             }
  1818.     }
  1819. }
  1820.  
  1821.     static void
  1822. prep_redo(num, cmd, c, nchar)
  1823.     long     num;
  1824.     int        cmd;
  1825.     int        c;
  1826.     int        nchar;
  1827. {
  1828.     ResetRedobuff();
  1829.     if (yankbuffer != 0)    /* yank from specified buffer */
  1830.     {
  1831.         AppendCharToRedobuff('\"');
  1832.         AppendCharToRedobuff(yankbuffer);
  1833.     }
  1834.     if (num)
  1835.         AppendNumberToRedobuff(num);
  1836.     AppendCharToRedobuff(cmd);
  1837.     if (c != NUL)
  1838.         AppendCharToRedobuff(c);
  1839.     if (nchar != NUL)
  1840.         AppendCharToRedobuff(nchar);
  1841. }
  1842.  
  1843. /*
  1844.  * check for operator active
  1845.  *
  1846.  * return TRUE if operator was active
  1847.  */
  1848.     static int
  1849. checkclearop()
  1850. {
  1851.     if (operator == NOP)
  1852.         return (FALSE);
  1853.     clearopbeep();
  1854.     return (TRUE);
  1855. }
  1856.  
  1857. /*
  1858.  * check for operator or Visual active
  1859.  *
  1860.  * return TRUE if operator was active
  1861.  */
  1862.     static int
  1863. checkclearopq()
  1864. {
  1865.     if (operator == NOP && VIsual.lnum == 0)
  1866.         return (FALSE);
  1867.     clearopbeep();
  1868.     return (TRUE);
  1869. }
  1870.  
  1871.     static void
  1872. clearopbeep()
  1873. {
  1874.     CLEAROP;
  1875.     beep();
  1876. }
  1877.  
  1878. /*
  1879.  * display, on the last line of the window, the characters typed before
  1880.  * the last command character, plus 'c1' and 'c2'
  1881.  */
  1882.     static void
  1883. premsg(c1, c2)
  1884.     int c1, c2;
  1885. {
  1886.     char_u    buf[40];
  1887.     char_u    *p;
  1888.  
  1889.     if (!p_sc || !(KeyTyped || c1 == -1 || c1 == ' '))
  1890.         return;
  1891.  
  1892.     cursor_off();
  1893.     msg_pos((int)Rows - 1, sc_col);
  1894.     if (c1 == -1)
  1895.         msg_outstr((char_u *)"          ");    /* look in comp_col() for the number of spaces */
  1896.     else
  1897.     {
  1898.         p = buf;
  1899.         if (opnum)
  1900.         {
  1901.             sprintf((char *)p, "%ld", (long)opnum);
  1902.             p = p + STRLEN(buf);
  1903.         }
  1904.         if (yankbuffer)
  1905.         {
  1906.             *p++ = '"';
  1907.             *p++ = yankbuffer;
  1908.         }
  1909.         if (c1 == Ctrl('W'))        /* ^W is in between counts */
  1910.         {
  1911.             *p++ = '^';
  1912.             *p++ = 'W';
  1913.             c1 = NUL;
  1914.         }
  1915.         else if (operator == 'z')
  1916.             *p++ = 'z';
  1917.         else if (operator)
  1918.             *p++ = opchars[operator - 1];
  1919.         if (Prenum)
  1920.         {
  1921.             sprintf((char *)p, "%ld", (long)Prenum);
  1922.             p = p + STRLEN(p);
  1923.         }
  1924.         *p = NUL;
  1925.         if (c1)
  1926.             STRCPY(p, transchar(c1));
  1927.         if (c2)
  1928.             STRCAT(p, transchar(c2));
  1929.         buf[10] = NUL;            /* truncate at maximal length */
  1930.         msg_outstr(buf);
  1931.     }
  1932.     setcursor();
  1933.     /* cursor_on(); */
  1934. }
  1935.